/*
 * Copyright 2008 Jens Meiss
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package hmcv.j2me.component;

import hmvc.component.IComponent;
import hmvc.controller.IController;
import hmvc.event.IEvent;
import hmvc.event.IEventListener;
import hmvc.model.IModel;
import hmvc.view.IView;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * @author jens.meiss
 * 
 * This is the basic IComponent-Implementation. Extend this Class to create you
 * own IComponent-Implementations.
 */
public class Component implements IComponent {

	private IController controller;

	private Hashtable hashtableEventListener = new Hashtable(5);

	private IModel model;

	private IComponent parentComponent;

	private boolean terminated = false;

	private Vector vectorChildComponents = new Vector(5);

	private Vector vectorTerminatedChilds = new Vector(5);

	private IView view;
	
	public Component(IController controller) {
		setController(controller);
		
		connectComponentParts();
		initComponentParts();
	}
	
	public Component(IController controller, IModel model) {
		setController(controller);
		setModel(model);

		connectComponentParts();
		initComponentParts();
	}
	
	public Component(IController controller, IModel model,
			IView view) {
		setController(controller);
		setModel(model);
		setView(view);
		
		connectComponentParts();
		initComponentParts();
	}
	
	public Component(IController controller, IView view) {
		setController(controller);
		setView(view);
		
		connectComponentParts();
		initComponentParts();
	}
	
	public Component(IComponent componentParent, IController controller, IModel model,
			IView view) {
		setParentComponent(componentParent);
		setController(controller);
		setModel(model);
		setView(view);
	}
	
	public void addChildComponent(IComponent component) {

		// TODO how to handle this Exception
		if (component.getController() == null) {
			throw new NullPointerException("no controller set");
		}
		
		component.connectComponentParts();

		vectorChildComponents.addElement(component);

		component.initComponentParts();
	}

	public IComponent addChildComponent(IController controller) {
		return addChildComponent(controller, null, null);
	}

	public IComponent addChildComponent(IController controller, IModel model) {
		return addChildComponent(controller, model, null);
	}

	public IComponent addChildComponent(IController controller, IModel model,
			IView view) {

		IComponent component = new Component(this, controller, model, view);
		
		addChildComponent(component);

		return component;
	}

	public IComponent addChildComponent(IController controller, IView view) {
		return addChildComponent(controller, null, view);
	}

	public void addTerminatedChildComponent(IComponent component) {
		vectorTerminatedChilds.addElement(component);
	}

	/**
	 * calls handleEvent() on all listeners the event
	 * 
	 * @param event
	 */
	protected final void callLocalListeners(IEvent event) {

		/*
		 * exists Listeners for this Event
		 */
		if (hashtableEventListener.containsKey(event.getClass().getName())) {

			/*
			 * get the Listener-Vector for this Event
			 */
			Vector vectorListeners = (Vector) hashtableEventListener.get(event
					.getClass().getName());

			/*
			 * throw the Event to all Listeners
			 */
			Enumeration enumerationListeners = vectorListeners.elements();
			while (enumerationListeners.hasMoreElements()) {
				IEventListener listener = (IEventListener) enumerationListeners
						.nextElement();
				listener.handleEvent(event);
			}
		}

	}

	public final void connectComponentParts() {

		/*
		 * connect Controller
		 */
		controller.setComponent(this);

		/*
		 * connect the Model
		 */
		if (model != null) {
			model.setController(controller);
		}

		/*
		 * connect the View
		 */
		if (view != null) {
			view.setController(controller);
		}
	}

	public boolean containsChildComponent(Class classOfController) {
//		return vectorChildComponents.contains(component);
		
		Enumeration enumeration = vectorChildComponents.elements();
		while (enumeration.hasMoreElements()) {
			IComponent component = (IComponent) enumeration.nextElement();
			
			if (component.getController().getClass().equals(classOfController)) {
				return true;
			}
		}
		
		return false;
	}

	public void fireEvent(IEvent event, boolean up, boolean local, boolean down) {

		if (down) {
			/*
			 * send the Event to all child Controller
			 */
			Enumeration enumeration = vectorChildComponents.elements();

			while (enumeration.hasMoreElements()) {
				Component component = (Component) enumeration.nextElement();
				component.fireEvent(event, false, true, true);
			}

		}

		/*
		 * don't call localListeners, the Component is shutting down
		 */
		if (local && (terminated == false)) {
			/*
			 * call all local Listeners of the Event
			 */
			callLocalListeners(event);
		}

		if (up) {
			/*
			 * send the Event to all parent Controller
			 */
			if (getParentComponent() != null) {
				getParentComponent().fireEvent(event, true, true, false);
			}
		}
	}

	public IController getController() {
		return controller;
	}

	public IModel getModel() {
		return model;
	}

	public IComponent getParentComponent() {
		return parentComponent;
	}

	public IView getView() {
		return view;
	}

	public final void initComponentParts() {
		
		/*
		 * call registerEvents() on the component-parts
		 */
		if (model != null) {
			model.registerEvents();
		}

		if (view != null) {
			view.registerEvents();
		}

		if (controller != null) {
			controller.registerEvents();
		}

		/*
		 * call the init() Method of every part
		 */
		if (model != null) {
			model.init();
			model.setInitialized(true);
		}

		if (view != null) {
			view.init();
			view.setInitialized(true);
		}

		if (controller != null) {
			controller.init();
			controller.setInitialized(true);
		}

	}

	public boolean isTerminated() {
		return terminated;
	}

	public void register(Class classOfEvent, IEventListener listener) {
		/*
		 * exists an Vector for holding Listeners of the Event
		 */
		Vector vectorListener = null;
		if (hashtableEventListener.containsKey(classOfEvent.getName())) {

			vectorListener = (Vector) hashtableEventListener.get(classOfEvent
					.getName());

		} else {

			/*
			 * create new Vector to hold Listener for this Event
			 */
			vectorListener = new Vector(3);
			hashtableEventListener.put(classOfEvent.getName(), vectorListener);
		}

		vectorListener.addElement(listener);
	}

	public void removeTerminatedChildComponents() {

		Enumeration enumeration = vectorTerminatedChilds.elements();

		while (enumeration.hasMoreElements()) {
			IComponent component = (IComponent) enumeration.nextElement();
			vectorChildComponents.removeElement(component);
		}

		vectorTerminatedChilds.removeAllElements();
	}

	public void setController(IController controller) {
		this.controller = controller;
	}

	public void setModel(IModel model) {
		this.model = model;
	}

	public void setParentComponent(IComponent parentComponent) {
		this.parentComponent = parentComponent;
	}

	public void setTerminated(boolean b) {
		terminated = b;
	}

	public void setView(IView view) {
		this.view = view;
	}

	public void terminate() {

		terminateChildComponents();
		removeTerminatedChildComponents();

		setTerminated(true);

		if (view != null) {
			view.setTerminated(true);
			view.terminate();
		}

		if (model != null) {
			model.setTerminated(true);
			model.terminate();
		}

		controller.setTerminated(true);
		controller.terminate();

		if (parentComponent != null) {
			parentComponent.addTerminatedChildComponent(this);
		}
	}

	/**
	 * calls terminate() on all childComponents
	 */
	private void terminateChildComponents() {

		Enumeration enumeration = vectorChildComponents.elements();
		while (enumeration.hasMoreElements()) {
			IComponent component = (IComponent) enumeration.nextElement();
			component.terminate();
		}

	}
}
